# download_file.py

import os

import requests
from tqdm import tqdm

from utils import log_recorder, send_email, setting

# 配置日志记录器
logger = log_recorder.setup_logger()

file_url = f"http://res.hytems.com/AutoGdp/{filename}"

# 文件下载成功时的邮件信息
mail_success_info = {
    'subject': f'总装GDP自动下载提示：“{ecnName}-{filename}”所有附件已下载成功（文件名：{filename}），请及时查收！',
    'message': (
        f"总装GDP文件【{filename}】已下载成功，请及时查收！（完整名称：{ecnName}-{filename}）\n"
        f"查收方式如下：\n"
        f"1、登录FTP服务器(服务器地址：res.hytems.com:2199 账号密码请联系IT人员);\n"
        f"2、【推荐】点击下边链接直接下载文件（如果无法链接无法点击，请复制到浏览器地址栏打开）：\n"
        f"文件下载地址：{file_url} \n"
        "如果上边链接无法访问，可访问 http://res.hytems.com/AutoGdp 下载文件。\n"
        "注：通过共享盘或访问FTP服务器均可查收(如何登录FTP服务器，请自行百度),如果以上方式无法查收，请直接点击链接下载。\n"
        "已下载文件在服务器上保留30天，过期后会自动清除。\n"
    )
}

# 文件下载失败时的邮件信息
mail_fail_info = {
    'subject': f'总装GDP自动下载提示：“{ecnName}-{filename}”下载失败，请登录GDP系统查看或等待程序自动重试！',
    'message': (
        f"由于对方接口通信异常，总装GDP文件【{filename}】（完整名称：{ecnName}-{filename}）下载失败，请登录GDP系统查看或等待程序自动重试！\n"
    )
}


def get_unique_file_path(save_path):
    """确保文件名唯一，如果文件已存在，则在文件名后添加序号"""
    base, extension = os.path.splitext(save_path)
    counter = 1
    while os.path.exists(save_path):
        save_path = f"{base}_{counter}{extension}"
        counter += 1
    return save_path


def get_file_size(url, headers):
    """获取待下载文件的总大小"""
    response = requests.head(url, headers=headers, allow_redirects=True)
    response.raise_for_status()  # 检查请求是否成功
    return int(response.headers.get('content-length', 0))


def download_file_chunks(url, save_path, headers, existing_file_size, total_file_size, chunk_size):
    """下载文件的每个部分并显示进度条"""
    with requests.get(url, headers=headers, stream=True, timeout=(5, 60)) as response:
        response.raise_for_status()  # 检查请求是否成功

        with open(save_path, 'ab') as file:  # 以追加模式打开文件
            with tqdm(total=total_file_size, initial=existing_file_size, unit='B', unit_scale=True,
                      desc=save_path) as pbar:
                for chunk in response.iter_content(chunk_size=chunk_size):
                    if chunk:
                        file.write(chunk)
                        pbar.update(len(chunk))  # 更新进度条


def download_file(url, save_path, max_retries=3, chunk_size=8192):
    """下载文件，支持断点续传和异常重试，并显示下载进度条，增加超时处理"""
    headers = {
        "User-Agent": "Mozilla/5.0",
        "X-HW-ID": "APP_021560_SZSHYTJD",
        "X-HW-APPKEY": "hRd1ex2Z4nk19nvK/cIrMA==",
        "content-type": "application/json"
    }

    existing_file_size = os.path.getsize(save_path) if os.path.exists(save_path) else 0
    logger.info(f"—— [Fr下载文件]已存在文件：{save_path}，大小：{existing_file_size}字节")

    # 为下载设置范围
    if existing_file_size > 0:
        headers['Range'] = f"bytes={existing_file_size}-"  # 从已有文件的大小处开始继续下载
        logger.info(f"——[Fr下载文件]从{existing_file_size}字节处开始下载...")
    else:
        logger.info("——[Fr下载文件]开始全新下载。")

    try:
        response = requests.head(url, headers=headers, allow_redirects=True)
        response.raise_for_status()  # 检查请求是否成功
        total_file_size = int(response.headers.get('content-length', 0))  # 获取要下载的文件大小

        logger.info(
            f"—— [Fr下载文件]待下载文件{filename}的总大小：{total_file_size}字节, {total_file_size / 1024 / 1024:.2f} MB")

        # 删除现有文件或获取新的保存路径
        if existing_file_size >= total_file_size:
            logger.warning(f"——[Fr下载文件]已存在的文件与要下载的文件相同，删除旧文件：{save_path}")
            os.remove(save_path)
            existing_file_size = 0  # 更新已存在文件大小为0以进行重新下载
        elif existing_file_size > total_file_size:
            logger.warning(f"——[Fr下载文件]已存在的文件大小大于目标文件，准备重命名：{save_path}")
            save_path = get_unique_file_path(save_path)  # 获取唯一文件名

        for attempt in range(max_retries):
            try:
                logger.info(f"——[Fr下载文件]正在下载：{url}，第 {attempt + 1} 次下载")
                download_file_chunks(url, save_path, headers, existing_file_size, total_file_size, chunk_size)

                logger.info("—— [Fr下载文件]文件下载完成！")
                send_email.send_mail(setting.main_info, mail_success_info)  # 发送成功邮件
                logger.info("—— [Fr下载文件]已发送邮件通知。")
                return True  # 下载成功

            except requests.exceptions.HTTPError as http_err:
                if http_err.response.status_code == 416:
                    logger.error("—— [Fr下载文件]请求范围不正确，调整后重试。")
                    os.remove(save_path)  # 删除文件以重新下载
                    return download_file(url, save_path, max_retries, chunk_size)  # 重新开始下载
                else:
                    logger.error(f"—— 下载过程中出现HTTP错误：{http_err}")
            except (requests.exceptions.RequestException, OSError) as err:
                logger.error(f"—— 下载过程中出现错误：{err}")

            if attempt < max_retries - 1:
                logger.info(f"—— 正在重试，第 {attempt + 2} 次...")
                continue  # 继续尝试下载
            else:
                logger.error("—— 达到最大重试次数，下载失败！")
                if os.path.exists(save_path):
                    os.remove(save_path)  # 清理部分已下载文件
                send_email.send_mail(setting.main_info, mail_fail_info)  # 发送失败邮件
                logger.error("—— [Fr下载文件]文件下载失败，已发送邮件通知。")
                raise  # 抛出异常

    except requests.exceptions.RequestException as err:
        logger.error(f"—— 获取文件大小失败：{err}")
        send_email.send_mail(setting.main_info, mail_fail_info)  # 发送失败邮件
        logger.error("—— [Fr下载文件]文件下载失败，已发送邮件通知。")
        raise  # 抛出异常


if __name__ == "__main__":
    # 可以在这里调用 download_file 函数进行测试
    download_url = setting.download_url
    file_save_path = setting.file_save_path
    save_path = os.path.join(file_save_path, filename)
    download_file(download_url, save_path)
